<?php
include_once('model/M_SQL.php');
/** 
 *  @author VGireev
 *	@REVISION:  $Id: M_Users.php 11 2011-12-18 12:52:37Z host.xserver@gmail.com $ 
 *  @brief Файл менеджер M_Users
 *  © myCompany 2011
 *
 *  Менеджер пользователей
 *  
 */

class M_Users
{	
	private static $instance;	//!< Экземпляр класса
	private		   $msql;		//!< Драйвер БД
	private		   $sid;		//!< Идентификатор текущей сессии
	private 	   $uid;		//!< Идентификатор текущего пользователя
	private		   $onlineMap;	//!< Карта пользователей online

	/**
	*	@brief Получение экземпляра класса
	*   @brief Результат - экземпляр класса M_SQL
	*/
	public static function Instance()
	{
		if (self::$instance == null)
			self::$instance = new M_Users();
			
		return self::$instance;
	}
/* ===================================================================== */
	// Конструктор
	public function __construct()
	{
		$this->msql		    = M_SQL::Instance();
		$this->sid			= null;
		$this->uid			= null;
		$this->onlineMap	= null;
	}
	
	// Очистка неиспользуемых сессий
	public function ClearSessions()
	{
		$min	= date('Y-m-d H:i:s', time() - 60 * 20); 			
		$t		= "time_last < '%s'";
		$where	= sprintf($t, $min);
		
		$this->msql->Delete('sessions', $where);
	}

	/**
	*	@brief 				- Авторизация
	*	@param $login 		- Логин
	*	@param $password 	- Пароль
	*	@param $remember 	- Нужно ли запомнить в куках
	*	@brief результат	- true или false
	*/
	public function Login($login, $password, $remember = true)
	{
		// Получаем пользователя из БД 
		$user = $this->GetByLogin($login);
		
		if ($user == null)
			return false;
		$id_user = $user['id_user'];
		
		// Проверяем пароль
		if ($user['password'] != md5($password))
			return false;
		
		// Запоминаем имя и md5(пароль)
		if ($remember) {
			$expire = time() + 3600 * 24 * 100;
			setcookie('login', $login, $expire);
			setcookie('password', md5($password), $expire);
		}		
				
		// Открываем сессию и запоминаем SID
		$this->sid = $this->OpenSession($id_user);
		
		return true;
	}

    /** 
     * @brief Выполнить регистрацию
     * 
     * @param $login     - Логин                          
     * @param $password  - Пароль                    
     * @param $remember  - Нужно ли запомнить в куках
     *               
     * @return  true	 - Вход удачный, false - Войти не получилось
     */
	public function Reg_user_add($login, $password, $user_name)
	{
		// Проверка: существует ли пользователь
		$t		= "SELECT id_user	,
						  login		,
						  password	,
						  id_role	,
						  name
				   FROM users 
				   WHERE login = '%s'";
				   
		$query	= sprintf($t, $login);
		$result	= $this->msql->Select($query);
		
		if ($result[0]['login'] == null) {
		
			// Если нет, то регистрируем
			$password	= md5($password);
			$object		= array('login'		=> $login,
						    	'password'	=> $password,
							    'id_role'	=> '2',
							    'name'		=> $user_name,
						       );
			
			$id = $this->msql->Insert('users', $object);
			
			if ($id > 0) {
				return true;
			}  
			else {
				return false;
			};		
		} else {
			return false;
		};
	}

    /** 
     * @brief Выход из системы
     * 
     * Очищаем все куки и сессии          
     */
	public function Logout()
	{
		setcookie('login', '', time() - 1);
		setcookie('password', '', time() - 1);
		
		unset($_COOKIE['login']);
		unset($_COOKIE['password']);
		unset($_SESSION['sid']);		
		
		$this->sid = null;
		$this->uid = null;
	}

    /** 
     * @brief Получить данные пользователя
     * 
     * Если id пользователя не задан, берется текущий       
     *      
     * @param $id_user - id пользователя                          
     * @return array данные пользователя
     */
	public function Get($id_user = null)
	{	
		// Если id_user не указан, берем его по текущей сессии
		if ($id_user == null)
			$id_user = $this->GetUid();
			
		if ($id_user == null)
			return null;
			
		// Возвращаем пользователя по id_user
		$t = "SELECT id_user	,
					 login		,
					 password	,
					 id_role	,
					 name
			  FROM users
			  WHERE id_user = '%d'";
			  
		$query	= sprintf($t, $id_user);
		$result = $this->msql->Select($query);
		return $result[0];		
	}

    /** 
     * @brief Получить данные пользователя
     * 
     * @param $login - Логин пользователя                         
     * @return array - Данные пользователя
     */
	public function GetByLogin($login)
	{	
		$t = "SELECT id_user	,
					 login		,
					 password	,
					 id_role	,
					 name
			 FROM users 
			 WHERE login = '%s'";
			 
		$query	= sprintf($t, mysql_real_escape_string($login));
		$result	= $this->msql->Select($query);
		
		return $result[0];
	}

    /** 
     * @brief Проверить наличие привилегии
     * 
     * Если id пользователя не задан, берется текущий пользователь          
     * 
     * @param $priv       - Логин пользователя    
     * @param $id_user    - ID пользователя            
     * @return true/false - Есть/нет привилегии
     */
	public function Can($priv, $id_user = null)
	{		
		if ($id_user == null)
		    $id_user = $this->GetUid();
		    
		if ($id_user == null)
		    return false;
		    
		$t = "SELECT count(*) as cnt FROM privs2roles p2r
			  LEFT JOIN users u ON u.id_role = p2r.id_role
			  LEFT JOIN privs p ON p.id_priv = p2r.id_priv 
			  WHERE u.id_user = '%d' AND p.name = '%s'";
		
		$query  = sprintf($t, $id_user, $priv);
		$result = $this->msql->Select($query);
		
		return ($result[0]['cnt'] > 0);
	}

    /** 
     * @brief Проверить, пользователь в онлайн или нет
     * 
     * @param $id_user    - ID пользователя            
     * @return true/false - Да/нет
     */
	public function IsOnline($id_user)
	{		
		if ($this->onlineMap == null) {	    
		    $t = "SELECT DISTINCT id_user FROM sessions";
			
		    $query  = sprintf($t, $id_user);
		    $result = $this->msql->Select($query);
		    
		    foreach ($result as $item)
		    	$this->onlineMap[$item['id_user']] = true;		    
		}
		
		return ($this->onlineMap[$id_user] != null);
	}

    /** 
     * @brief Получить id текущего пользователя
     * 
     * Текущий пользователь определяется по текущей сессии пользователя     
     *     
     * @return id текущего пользователя
     */
	public function GetUid()
	{	
		// Проверка кеша
		if ($this->uid != null)
			return $this->uid;	

		// Берем по текущей сессии
		$sid = $this->GetSid();
				
		if ($sid == null)
			return null;
			
		$t		 = "SELECT id_user FROM sessions WHERE sid = '%s'";
		$query	 = sprintf($t, mysql_real_escape_string($sid));
		$result  = $this->msql->Select($query);
				
		// Если сессия не найдена - значит пользователь не авторизован
		if (count($result) == 0)
			return null;
			
		// Если сессия найдена - запомним ее
		$this->uid = $result[0]['id_user'];
		return $this->uid;
	}

    /** 
     * @brief Получить id текущей сессии пользователя
     * 
     *     
     * @return id текущей сессии пользователя
     */
	private function GetSid()
	{
		/// Проверка кеша
		if ($this->sid != null)
			return $this->sid;
	
		// Ищем SID в сессии
		$sid = $_SESSION['sid'];
								
		// Если нашли, попробуем обновить time_last в базе
		// Проверим, есть ли сессия в базе
		if ($sid != null) {
			$session 			  = array();
			$session['time_last'] = date('Y-m-d H:i:s'); 			
			$t 					  = "sid = '%s'";
			$where 				  = sprintf($t, mysql_real_escape_string($sid));
			$affected_rows		  = $this->msql->Update('sessions', $session, $where);

			if ($affected_rows == 0) {
				$t 		= "SELECT count(*) FROM sessions WHERE sid = '%s'";		
				$query  = sprintf($t, mysql_real_escape_string($sid));
				$result = $this->msql->Select($query);
				
				if ($result[0]['count(*)'] == 0)
					$sid = null;			
			}			
		}		
		
		// Нет сессии? Ищем логин и md5(пароль) в куках
		// То есть пробуем переподключиться
		if ($sid == null && isset($_COOKIE['login'])) {
			$user = $this->GetByLogin($_COOKIE['login']);
			
			if ($user != null && $user['password'] == $_COOKIE['password'])
				$sid = $this->OpenSession($user['id_user']);
		}
		
		// Запоминаем в кеш
		if ($sid != null)
			$this->sid = $sid;
		
		// Возвращаем SID
		return $sid;		
	}

    /** 
     * @brief Создать новую сессию для пользователя
     * 
     * @param $is_user id пользователя
     * @return id текущей сессии пользователя
     */
	private function OpenSession($id_user)
	{
		// Генерируем SID
		$sid = $this->GenerateStr(10);
				
		// Вставляем SID в БД
		$now					= date('Y-m-d H:i:s'); 
		$session 				= array();
		$session['id_user'] 	= $id_user;
		$session['sid'] 		= $sid;
		$session['time_start']  = $now;
		$session['time_last'] 	= $now;				
		$this->msql->Insert('sessions', $session); 
				
		// Регистрируем сессию в PHP сессии
		$_SESSION['sid'] = $sid;				
				
		// Возвращаем SID
		return $sid;	
	}

    /** 
     * @brief Сгенерировать случайную строку
     * 
     * @param $length длина строки
     * @return случайная строка
     */
	private function GenerateStr($length = 10) 
	{
		$chars	= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRQSTUVWXYZ0123456789";
		$code	= "";
		$clen	= strlen($chars) - 1;  

		while (strlen($code) < $length) 
            $code .= $chars[mt_rand(0, $clen)];  

		return $code;
	}

}
